package testedge

import (
	"context"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"net"
	"os"
	"runtime"
	"strings"
	"sync"
	"time"
)

var (
	Lock sync.Mutex
	TimeAfterFlag = false
	Groupflags sync.WaitGroup
)

var PoolOffchannel = make(chan struct{})

//var Lock sync.Mutex
//var  MaxBuffer = 4096

const (
	LINE string = "line"
	HOLE string = "hole"
)

const (
	SHORT string = "short"
	LONG string = "long"
)

const (
	ADD string = "add"
	DEL string = "del"
	Exit string = "EXIT"
)

var (
	ConnectCount      int
	DialFailCount     int
	WriteFailCount    int
	ReadFailCount     int
	WriteTimeOutCount int
	ReadTimeOutCount  int
	TestOk            int
	CliTotals         int = 0
)

var (
	CliTotal = &CliTotals
)

type Itestedge interface {
	//服务端启动程序
	Server()
	//客户端启动程序
	Client()
	//从conn接收数据并通过conn发送
	ServerHandel(conn net.Conn)
	//从conn接收的数据发送到文件中
	ClientHandel(buffer []byte, readLenTmp int) error
	//客户端短链接测试方法
	ShortClient(buffer []byte, readLenTmp int) error
	//客户端长链接测试方法
	LongClient(buffer []byte, readLenTmp int) error
	//超时处理
	Timeout() bool
	//读取文件
	ReadHexFile() (int, []byte, error)
	//心跳计时
	HeartBetting(conn net.Conn, channel chan byte)
	//超时计算
	LongShortTimeOut(conn net.Conn, recvDataLen int, buffer []byte)
	//检测长链接心跳
	DetectBeating(n []byte, message chan byte)
	//多协程死锁
	locks(*int)
	//实现file文件的数据解析
	HandelStringToByte(buffer []byte) (int, []byte)
	//获取分离host和端口
	GetDivHostPort(adds string) []string
	//获取合成host和端口
	GetUnionHostPort(adds []string) string
}

type Connects struct {
	//读取数据到conn的文件路径
	SrcFilePath string
	//连接网络
	Network string
	//链接数
	LinkNum int
	//链接时间
	Times int
	//连接超时
	ClientConnectTimeout int
	//读写数据时间超时
	ReadWriteDataTimeout int
	//单双向
	Direction string
	//长短链接
	ShorLongFlag string
	//客户端或服务端
	Mode string
	//最大缓存区
	MaxBuffer int64
	//连接地址ip
	Host string
	//连接地址端口
	Port string
}

func InitConnect() *Connects {
	//初始化连接属性
	a := &Connects{
		SrcFilePath:          "/home/sdt/kaiyue/kaiyue_testedge/q_gate_ok.txt",
		Network:              "tcp4",
		Host: 				  "localhost",
		Port: 				  "8989",
		LinkNum:              1,
		Times:                50,
		ClientConnectTimeout: 5,
		ReadWriteDataTimeout: 5,
		Direction: 			  "",
		ShorLongFlag: 		  "",
		MaxBuffer: 			  4 * 1024,
	}
	return a
}

/*
实现协程的信息同步
 */
func SyncThread(flagtype string) {
	switch flagtype {
	case ADD:
		Groupflags.Add(1)
	case DEL:
		Groupflags.Done()
	case Exit:
		runtime.Goexit()
	default:
		fmt.Println("group tine close err!")
		return
	}
}

/*
对localhost:8080 字符串进行分割功能
 */
func (a Connects) GetDivHostPort(adds string) []string {
	//localhost:8080
	var div = strings.Split(adds, ":")
	return div
}

/*
对[localhost, 8080] 字符串数组进行组合功能
*/
func (a Connects) GetUnionHostPort(adds []string) string {
	//localhost:8080
	var union = strings.Join(adds, ":")
	return union
}

/*
服务端
*/
func (a Connects) Server(ctx context.Context) {
	fmt.Println("sever init!")
	var netaddres []string
	addrs, err := net.ResolveTCPAddr(a.Network, a.GetUnionHostPort(append(netaddres, "", a.Port)))
	fmt.Println("addrs:", addrs)
	if err != nil {
		fmt.Println("resolveIPAddr is err!:", err)
		return
	}
	conn, err := net.ListenTCP(a.Network, addrs)
	if err != nil {
		fmt.Println("Listen is err!:", err)
		return
	}

	var connections int64
	fmt.Println("waiting for connect.....")
	for {
		conn, err := conn.AcceptTCP()
		if err != nil {
			fmt.Println("accept is err!", err)
			return
		}
		select {
		case <- ctx.Done():
			return
		default:
			go a.ServerHandel(conn, ctx)
		}
		//go a.ServerHandel(conn, ctx)
		connections += 1
		fmt.Println("has creat connect number is :", connections)
	}
}


/*
等待crtl+c关闭通道
*/
func checkCrtlchannel(pool *Pool) {
	<- PoolOffchannel
	pool.Shutdown()
}

/*
客户端
*/
func (a Connects) Client(ctx context.Context) {
	fmt.Println("client init!")
	var cacheNum int = 30
	var fixNum int = 30
    pool := New(a.LinkNum) // 创建一个协程池
    pool.Start()      // 启动协程池
	//读取到的文件数据
	readLenTmp, buffer, err:= a.ReadHexFile()
	if err != nil {
		fmt.Println("readfile err!", err)
		return
	}
	defer pool.Shutdown()
	go checkCrtlchannel(pool) //监控是否发生crtl+c事件

	//建立多线程
	task := func(buffer []byte, readLenTmp int, ctx context.Context) Task {
		return func() {
			err := a.ClientHandel(buffer, readLenTmp, ctx)
			if err != nil {
				fmt.Println("执行客户端协程失败！")
			}
			cacheNum--
		}
	}

	//提交任务到协程池中
	for i := 0; i < a.LinkNum + cacheNum; i++ {
		pool.Run(task(buffer, readLenTmp, ctx))
	}
	//当实际协程少于10时开始添加任务
	for {
		if cacheNum < 10 {
			for i := 0; i < fixNum-cacheNum; i++ {
				pool.Run(task(buffer, readLenTmp, ctx))
				cacheNum++
			}
		}
	}

	//建立多线程
	//for i := 1; i <= a.LinkNum; i++ {
	//	//对每个协程增加计数
	//	SyncThread(ADD)
	//	go a.ClientHandel(buffer, readLenTmp, ctx)
	//}
    // 等待所有任务完成并关闭协程池
}

func (a Connects) startServer(conn net.Conn, buffer []byte) (string, error){
	var err error = nil
	err = conn.SetReadDeadline(time.Now().Add(time.Duration(a.ReadWriteDataTimeout) * time.Second))
	if err != nil {
		fmt.Println("SetReadDeadline err :", err)
		return "", err
	}
	readLenTmp, err1 := conn.Read(buffer)
	if err1 != nil {
		if err1 == io.EOF {
			return "", nil
		}
		fmt.Println("读取数据失败!, err:", err1)
		return "", err1
	}

	if a.Direction == HOLE {
		switch a.ShorLongFlag {
		case SHORT:
			return a.ShorLongFlag, nil
		case LONG:
			return a.ShorLongFlag, nil
		default:
			return "", fmt.Errorf("mode err!")
		}

	} else {
		err = conn.SetWriteDeadline(time.Now().Add(time.Duration(a.ReadWriteDataTimeout) * time.Second))
		if err != nil {
			return "", fmt.Errorf("SetReadDeadline err:%s", err.Error())
		}

		_, err := conn.Write(buffer[:readLenTmp])
		if err != nil {
			return "", fmt.Errorf("send failed!, err:%s",err.Error())
		}

		switch a.ShorLongFlag {
		case SHORT:
			return a.ShorLongFlag, nil
		case LONG:
			return a.ShorLongFlag, nil
		default:
			return "", fmt.Errorf("mode err!")
		}
	}
}

/*
读取socket内容到socket中
*/
func (a Connects) ServerHandel(conn net.Conn, ctx context.Context) {
	var err error
	var mode string = ""
	defer conn.Close()

	//创建最大缓存区，并设置超时
	buffer := make([]byte, a.MaxBuffer)

	//LineFlag:
	for {
		select {
		case <- ctx.Done():
			return
		default:
			mode, err = a.startServer(conn, buffer)
		}

		if err != nil {
			fmt.Printf("服务端发送错误 err:%s\n", err.Error())
			return
		}
		switch mode {
		case SHORT: //短连接每次发完后断开
			return
		case LONG: //长连接在数据发完后不进行锻练
			continue
		default:
			return
		}
	}
}

/*
超时计算
*/
func (a Connects) LongShortTimeOut(conn net.Conn, recvDataLen int, buffer []byte) {
	//根据不同的长短链接进行不同的超时计算
	if a.ShorLongFlag == LONG {
		message := make(chan byte)
		//心跳计数
		go a.HeartBetting(conn, message)
		//检测是否有数据进入
		go a.DetectBeating(buffer[:recvDataLen], message)

	} else if a.ShorLongFlag == SHORT {
		//短连接直接调用set函数进行超时计算
		err := conn.SetReadDeadline(time.Now().Add(time.Duration(a.ReadWriteDataTimeout) * time.Second))
		if err != nil {
			fmt.Println("short read timeout!")
			return
		}
		select {
		case <-time.After(time.Duration(a.Times) * time.Second):
			fmt.Println("short time after!")
			conn.Close()
		}
	}
}

/*
对多协程中的统计数进行锁定
 */
func (a Connects) locks(countTypes *int)  {
	Lock.Lock()
	*countTypes += 1
	defer Lock.Unlock()
}

/*
对多协程中的统计数进行锁定
*/
func (a Connects) lockPacket(countTypes int)  {
	Lock.Lock()
	*CliTotal += countTypes
	defer Lock.Unlock()
}

/*
读取文件中内容到socket中
*/
func (a Connects) ClientHandel(buffer []byte, readLenTmp int, ctx context.Context) error {
	//defer SyncThread(DEL)
	var err error
	if a.ShorLongFlag == SHORT {
		//短连接进行链接测试
		err = a.ShortClient(buffer, readLenTmp, ctx)
		if err != nil {
			fmt.Println("shorclient err :", err)
			return err
		}

	} else {
		//长连接进行连接测试
		err = a.LongClient(buffer ,readLenTmp, ctx)
		if err != nil {
			fmt.Println("longclient err:", err)
			return err
		}
	}
	return err
}

func (a Connects) StartShortClient(buffer []byte, readLenTmp int, netaddres []string, readBuffer []byte) error {
	var conn net.Conn
	var err error

	conn, err = net.DialTimeout(a.Network,a.GetUnionHostPort(append(netaddres, a.Host, a.Port)), time.Duration(a.ClientConnectTimeout) * time.Second)
	if err != nil {
		conn, err = net.DialTimeout(a.Network,a.GetUnionHostPort(append(netaddres, a.Host, a.Port)), time.Duration(a.ClientConnectTimeout) * time.Second)
		if err != nil {
			fmt.Println("dialip failed!", err)
			a.locks(&DialFailCount)
			return err
		}
	}
	defer conn.Close()

	a.locks(&ConnectCount)

	// 写数据到conn中
	writeBufferLen, err := conn.Write(buffer[:readLenTmp])
	if err != nil {
		fmt.Print("write is err!")
		a.locks(&WriteFailCount)
		return err
	}
	a.lockPacket(writeBufferLen)

	if a.Direction == LINE {
		a.locks(&TestOk)
		return nil
	} else {
		_, err := conn.Read(readBuffer)
		//a.LongShortTimeOut(conn, recvLenTmp, buffer)
		if err != nil {
			fmt.Println("read data is err!", err)
			a.locks(&ReadFailCount)
			return err
		}

		a.locks(&TestOk)
	}
	return nil
}

func (a Connects) ShortClient(buffer []byte, readLenTmp int, ctx context.Context) error{
	var netaddres []string
	readBuffer := make([]byte, readLenTmp)

	select {
	case <- ctx.Done():
		return nil
	default:
		a.StartShortClient(buffer, readLenTmp, netaddres, readBuffer)
	}
	return nil
}

func (a Connects) StartLongClient (conn net.Conn, buffer []byte, readLenTmp int, readBuffer []byte) error{
	// 写数据到conn中
	writeBufferLen, err := conn.Write(buffer[:readLenTmp])
	if err != nil {
		fmt.Println("write is err!", err)
		a.locks(&WriteFailCount)
		return err
	}
	a.lockPacket(writeBufferLen)

	if a.Direction == LINE {
		a.locks(&TestOk)
		return nil
	} else {
		_, err = conn.Read(readBuffer)
		if err != nil {
			fmt.Println("read data is err!", err)
			a.locks(&ReadFailCount)
			return err
		}
		a.locks(&TestOk)
	}

	return nil
}

//处理超时
func (a Connects) DealTimeOut () (net.Conn, error) {
	var err error
	var netaddres []string
	conn, err := net.DialTimeout(a.Network, a.GetUnionHostPort(append(netaddres, a.Host, a.Port)), time.Duration(a.ClientConnectTimeout) * time.Second)
	if err != nil {
		fmt.Println("dialip failed!", err)
		a.locks(&DialFailCount)
		return nil, err
	}

	return conn, nil
}

func (a Connects) LongClient (buffer []byte, readLenTmp int, ctx context.Context) error{
	var conn net.Conn = nil
	var err error = nil

	select {
		case <- ctx.Done():
			return nil
		default:
			conn, err = a.DealTimeOut()
	}
	if err != nil {
		return fmt.Errorf("链接超时失败!")
	}
	defer conn.Close()

	a.locks(&ConnectCount)
	readBuffer := make([]byte, readLenTmp)

	for {
		select {
		case <- ctx.Done():
			return nil
		default:
			a.StartLongClient(conn, buffer, readLenTmp, readBuffer)
		}
	}
}

func (a Connects) Timeout() bool {
	var timeoutflag bool
	select {
	case <-time.After(time.Duration(a.Times) * time.Second):
		timeoutflag = true
		break
	default:
		timeoutflag = false
		break
	}
	return timeoutflag
}

func (a Connects) ReadHexFile() (int, []byte, error){
	info, err := os.Stat(a.SrcFilePath)
	if  err != nil {
		if os.IsNotExist(err) {
			fmt.Println("file is not exited!",err)
			return 0, nil, err
		}
	}

	if info.Size() > a.MaxBuffer {
		return 0, nil, errors.New("Beyond maxbuffer!")
	}

	filefd, err := os.Open(a.SrcFilePath)
	buffer := make([]byte, info.Size())

	if err != nil {
		fmt.Print("file open err!", err)
		return 0, nil, err
	}
	defer filefd.Close()

	_, err = filefd.Read(buffer)
	if err != nil {
		fmt.Println("read err:", err)
		return 0, nil, err
	}
	readLenTmp, HexDatebuffer, err := a.HandelStringToByte(buffer)
	if err != nil {
		fmt.Println("HandelStringToByte, err:", err)
		return 0, nil, err
	}
	return readLenTmp, HexDatebuffer, err
}

func (a Connects) HandelStringToByte(buffer []byte) (int, []byte, error) {
	//匿名函数实现分割
	f := func(c rune) bool {
		return c == ',' || c == '\r' || c == '\n'
	}

	//对字符串按照分隔符进行分割，并重新进行编码
	listd := strings.FieldsFunc(string(buffer), f)
	data, err := hex.DecodeString(strings.Join(listd, ""))
	if err != nil {
		fmt.Println("handel data err!:", err)
		return 0, nil, fmt.Errorf("读取二进制文件失败!")
	}
	return len(data), data, nil
}

/*
dv ig
*/
func (a Connects) HeartBetting(conn net.Conn, channel chan byte) {
	//监控数据通道是否读取到数据并进行超时设计
	select {
	case <-channel:
		err := conn.SetReadDeadline(time.Now().Add(time.Duration(a.ReadWriteDataTimeout) * time.Second))
		if err != nil {
			fmt.Println("long read err", err)
			return
		}
		fmt.Println("long read timeout")
		break
	case <-time.After(time.Duration(a.Times) * time.Second):
		fmt.Println("long time after!")
		conn.Close()
		break
	}
}

func (a Connects) DetectBeating(n []byte, message chan byte)  {
	for _, v := range n{
		message <- v
	}
	close(message)
}


